#pragma once

#include <stdint.h>
#include <vector>
namespace Hazel
{

enum class ShaderDataType
{
    None = 0,
    Float,
    Float2,
    Float3,
    Float4,
    Mat3,
    Mat4,
    Int,
    Int2,
    Int3,
    Int4,
    Bool
};

static uint32_t ShaderDataTypeSize(ShaderDataType type)
{
    switch (type)
    {
    case ShaderDataType::Float:
        return 4;
    case ShaderDataType::Float2:
        return 4 * 2;
    case ShaderDataType::Float3:
        return 4 * 3;
    case ShaderDataType::Float4:
        return 4 * 4;
    case ShaderDataType::Mat3:
        return 4 * 3 * 3;
    case ShaderDataType::Mat4:
        return 4 * 4 * 4;
    case ShaderDataType::Int:
        return 4;
    case ShaderDataType::Int2:
        return 4 * 2;
    case ShaderDataType::Int3:
        return 4 * 3;
    case ShaderDataType::Int4:
        return 4 * 4;
    case ShaderDataType::Bool:
        return 4;
    }
    assert(false);
    return 0;
}

struct BufferElement
{
    std::string Name;
    ShaderDataType Type;
    uint32_t Size;
    size_t Offset;
    bool Normalized;

    BufferElement()
    {
    }
    BufferElement(ShaderDataType type, const std::string &name, bool normalized = false)
        : Name(name), Type(type), Size(ShaderDataTypeSize(type)), Offset(0), Normalized(normalized)
    {
    }

    uint32_t GetComponentCount() const
    {
        return ShaderDataTypeSize(Type) / 4;
    }
};

class BufferLayout
{
  public:
    BufferLayout()
    {
    }
    BufferLayout(const std::initializer_list<BufferElement> &elements) : m_Elements(elements)
    {
        CalculateOffsetAndStride();
    }

    inline uint32_t GetStride() const
    {
        return m_stride;
    }
    inline const std::vector<BufferElement> &GetElements() const
    {
        return m_Elements;
    }

    std::vector<BufferElement>::const_iterator begin() const
    {
        return m_Elements.begin();
    }
    std::vector<BufferElement>::const_iterator end() const
    {
        return m_Elements.end();
    }

  private:
    void CalculateOffsetAndStride()
    {
        uint32_t offset = 0;
        m_stride = 0;
        for (auto &element : m_Elements)
        {
            element.Offset = offset;
            offset += element.Size;
            m_stride += element.Size;
        }
    }

  private:
    std::vector<BufferElement> m_Elements;
    uint32_t m_stride;
};

class VertexBuffer
{
  public:
    virtual ~VertexBuffer() = default; // 纯虚析构函数

    // virtual void SetData(const void* data, int size) = 0;
    virtual void Bind() const = 0;
    virtual void Unbind() const = 0;

    virtual void SetLayout(const BufferLayout &layout) = 0;
    virtual const BufferLayout &GetLayout() const = 0;

    virtual void SetData(const void *data, uint32_t size) = 0;
    
    static Ref<VertexBuffer> Create(float *vertices, uint32_t size);
    static Ref<VertexBuffer> Create(uint32_t size);
};

class IndexBuffer
{
  public:
    virtual ~IndexBuffer() = default; // 纯虚析构函数
    virtual void Bind() const = 0;
    virtual void Unbind() const = 0;

    virtual uint32_t GetCount() const = 0;

    static Ref<IndexBuffer> Create(uint32_t *Indices, uint32_t count);
};
} // namespace Hazel